﻿using Games.Common;
using Games.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Games.ChallengeMemory
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class GamePage : ContentPage
    {
        IDataBase database = DependencyService.Get<IDataBase>();
        Game game;
        private readonly GameInfo gameInfo;
        const int CellSpacing = 2;
        int cellSize;
        double layoutWidth, layoutHeight;
        GameLevel boxLevel;
        private bool isStarted = false;

        internal GamePage(GameInfo gameInfo)
        {
            InitializeComponent();
            BindingContext = this.gameInfo = gameInfo;
            game = new Game();
            LoadAsync().Wait();
            database.CreateTable<ChallengeMemoryRecord>();
        }
        async Task LoadAsync()
        {
            var gameArgs = await database.GetFirstOrDefaultAsync<GameArgs>(x => x.GameID == gameInfo.ID).ConfigureAwait(false);
            if(gameArgs != null)
            {
                boxLevel = BaseArgs.Convert<GameLevel>(gameArgs);
            }
            else
            {
                boxLevel = new GameLevel()
                {
                    GameID = gameInfo.ID,
                    //Version = gameInfo.Version,
                    Size = 4,
                    TCount = 1
                };
            }
        }
        async Task SaveArgsAsync()
        {
            await database.SaveAsync(boxLevel.ToGameArgs());
        }
        async void OnLayoutSizeChanged(object sender, EventArgs args)
        {
            Layout layout = sender as Layout;
            layoutWidth = layout.Width;
            layoutHeight = layout.Height;
            game.Init(boxLevel.Size, boxLevel.TCount);
            await InitLayout();
        }

        async Task InitLayout()
        {
            gameInfo.Level = $"{boxLevel.Size}*{boxLevel.Size}=>{boxLevel.TCount}";
            gameInfo.Frame = 0;
            var sizew = (int)((layoutWidth + CellSpacing) / boxLevel.Size);
            var sizeh = (int)((layoutHeight + CellSpacing) / boxLevel.Size);
            cellSize = Math.Min(sizew, sizeh);
            var laySize = game.Size * cellSize - CellSpacing;

            var xMargin = (layoutWidth - laySize) / 2.0;
            var yMargin = (layoutHeight - laySize) / 2.0;
            absoluteLayout.Margin = new Thickness(xMargin, yMargin);
            absoluteLayout.WidthRequest = absoluteLayout.HeightRequest = laySize;
            var count = game.Size * game.Size;

            while(absoluteLayout.Children.Count > count)
            {
                absoluteLayout.Children.RemoveAt(0);
            }

            while(absoluteLayout.Children.Count < count)
            {
                GameCell lifeCell = new GameCell();
                absoluteLayout.Children.Add(lifeCell);
                lifeCell.Tapped += this.LifeCell_Tapped;
            }

            for(int i = 0; i < count; i++)
            {
                GameCell cell = (GameCell)absoluteLayout.Children[i];
                cell.ID = i;
                var col = i % game.Size;
                var row = i / game.Size;
                Rectangle rect = new Rectangle(col * cellSize,
                                               row * cellSize,
                                               cellSize - CellSpacing,
                                               cellSize - CellSpacing);
                cell.Source = null;
                cell.Show();
                await cell.ToggleDark(true);
                AbsoluteLayout.SetLayoutBounds(cell, rect);
            }
            BrightLast();
            await Bright();
            isStarted = false;
        }

        private static readonly ImageSource focus = ImageSource.FromFile("flip_focus.png");
        async void BrightLast()
        {
            var color = GameCell.GetColor();
            foreach(var item in game.GetLast())
            {
                GameCell cell = (GameCell)absoluteLayout.Children[item];
                await cell.ToggleBright(color, true);
                cell.Source = focus;
            }
        }

        async Task Bright()
        {
            var color = GameCell.GetColor();
            var tasks = new List<Task>();
            foreach(var item in game.GetCurrent())
            {
                GameCell cell = (GameCell)absoluteLayout.Children[item];
                tasks.Add(cell.ToggleBright(color));
            }
            await Task.WhenAll(tasks);
        }
        async Task Dark()
        {
            var tasks = new List<Task>();
            foreach(var item in game.GetLast())
            {
                GameCell cell = (GameCell)absoluteLayout.Children[item];
                tasks.Add(cell.ToggleDark());
            }
            await Task.WhenAll(tasks);
        }

        private async Task Warn()
        {
            Vibration.Vibrate(200);
            var lasts = game.GetLast();
            foreach(var item in lasts)
            {
                GameCell cell = (GameCell)absoluteLayout.Children[item];
                await cell.ToggleBright(Color.Red);
            }
        }

        private async Task Flash(int id)
        {
            GameCell cell = (GameCell)absoluteLayout.Children[id];
            await cell.ShowFlash();
            cell.Source = null;
        }
        async void LifeCell_Tapped(object sender, EventArgs e)
        {
            if(game.IsOver)
            {
                return;
            }
            var cell = sender as GameCell;
            if(cell == null)
            {
                return;
            }
            if(!isStarted)
            {
                if(cell.Source == null)
                {
                    return;
                }
                isStarted = true;
                TimeCounter.Start();
            }
            var tasks = new List<Task>();
            await game.Send(cell.ID, Warn, Flash);
            if(game.IsOver)
            {
                await Task.WhenAll(tasks);
                await ShowFinish();
            }
            else if(gameInfo.Frame != game.Frame)
            {
                gameInfo.Frame = game.Frame;
                await Dark();
                await Bright();
            }
        }

        async Task ShowFinish()
        {
            await SaveArgsAsync();
            var record = await SaveRecordAsync();
            await DisplayAlert("加油", $"游戏结束 {record.Message}", "OK");

            game.Init(boxLevel.Size, boxLevel.TCount);
            await InitLayout();
        }
        async Task<Record> SaveRecordAsync()
        {
            var dur = TimeCounter.Stop();
            var cRecord = new ChallengeMemoryRecord()
            {
                Frame = gameInfo.Frame,
                Size = game.Size,
                TCount = game.TCount,
                GameID = gameInfo.ID,
                CreateTime = DateTime.Now,
                Duration = dur
            };

            await database.SaveRecordByDiff<ChallengeMemoryRecord>(cRecord, 5,
                o => o.Size == cRecord.Size && o.TCount == cRecord.TCount,
                list => list.OrderByDescending(x => x.Frame).ThenBy(x => x.Duration).ThenByDescending(x => x.ID),
                o => o.Frame < cRecord.Frame || (o.Frame == cRecord.Frame && o.Duration > cRecord.Duration)
            );
            return cRecord.Convert();
        }
        void LevelClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("levels"))
            {
                var view = CreateDiffs();
                mask.SetSize("levels", 0.8, 0.5);
                mask.Add("levels", "选择难度", view);
            }
            mask.ShowWithCancel("levels");
        }

        ScrollView CreateDiffs()
        {
            var grid = new Grid
            {
                Padding = new Thickness(20, 5),
            };
            var layopt = new LayoutOptions(LayoutAlignment.Fill, true);
            var margin = new Thickness(1);
            for(int i = 4; i <= 6; i++)
            {
                grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
                for(int j = 1; j <= 3; j++)
                {
                    var btn = new Button { Text = $"{i}*{i}->{j}", Margin = margin, HorizontalOptions = layopt };
                    grid.Children.Add(btn, i - 4, j - 1);
                    var size = i;
                    var tcount = j;
                    btn.Clicked += (s, e) => LevelSelect(size, tcount);
                }
            }
            var view = new ScrollView
            {
                Content = grid
            };
            return view;
        }
        async void LevelSelect(int size, int tcount)
        {
            mask.Hide();
            if(boxLevel.Size != size || boxLevel.TCount != tcount)
            {
                boxLevel.Size = size;
                boxLevel.TCount = tcount;
                await SaveArgsAsync();
            }
            game.Init(boxLevel.Size, boxLevel.TCount);
            await InitLayout();
        }

        void OnAboutButtonClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("about"))
            {
                var aboutView = ControlUtil.GetAbout(gameInfo);
                mask.Add("about", gameInfo.Name, aboutView);
            }
            mask.ShowWithCancel("about", "确定");
        }
        void OnRecordClicked(object sender, EventArgs args)
        {
            //var sorter = new Sorter<ChallengeMemoryRecord>(q => q.OrderByDescending(x => x.TCount).ThenByDescending(x => x.Size).ThenByDescending(x => x.Frame).ThenBy(x => x.Duration).ThenBy(x => x.ID));
            Navigation.PushAsync(new RecordPage(gameInfo.Name, new RecordViewModel<ChallengeMemoryRecord>(null)));
        }

        private void TapToBack(object sender, EventArgs e)
        {
            Navigation.PopAsync();
        }
    }
}